#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <termios.h>	/* POSIX 终端控制定义*/ 
#include <signal.h>
#include <sys/time.h>   //select time

#include "iec10x.h"
#include "STD.h"

static int ttyfd = 0;
iec10x_t   demobus;
iec10x_t   parsebus;

iec101cfg_t cfg = {0};
iec101cfg_t parsecfg = {0};

#define HAL_DEBUG 0

char parserbuf[4096] ;
int buftail = 0 ;


static int16_t  STDallnums = 8 ;                  // 实际 STD 个数
static uint32_t  STDaddrlist[8] = {1,2,3,4,5,6,7,8}; // 实际 STD 地址列表
static float     STDvalue[8] = {1.11f,2.22f,3.33f,4.44f,5.55f,6.66f,7.77f,8.88f};

int32_t STD_info(void * bus,uint32_t ** addrlist,int * num)
{
	*addrlist = STDaddrlist;
	*num  = 8 ;
	return 0;
}

int32_t STD_get(void * bus, uint32_t  infoaddr , void * value )
{
	STD0T_t * info = (STD0T_t *) value;
	memcpy(info->STD.val,&STDvalue[infoaddr-1],4);
	info->QDS.val  = 0;
	return 0; 
}


void iec10x_puts(unsigned char * info , int len)
{
	if (ttyfd) {
		write(ttyfd,info,len);
	}
	#if HAL_DEBUG
	printf("\r\ntx %d bytes\n", len);
	for (int i = 0; i < len ; ) {
		printf("%02x ", info[i]);
		if (((++i)& 0x1f) == 0) 
			printf("\r\n");
	}

	if (len & 0x1f) {
		printf("\r\n");
	}
	#endif

	puts(parserbuf);
	buftail = 0;
}

/**
  * @brief    串口设置
  * @param    fd       : 已打开的串口句柄
  * @param    nspeed   : 波特率，9600，115200 等
  * @param    nbits    : 数据宽度，7/8/9
  * @param    nevent   : 数据校验，'O'(奇)/'E'(偶)/'N'(无)
  * @param    nstop    : 停止位，1/2
  * @return   设置成功返回 0 ，否则返回其他
*/
static int usart_set(int fd, int nspeed, int nbits, char nevent, int nstop)
{
	struct termios newtio, oldtio;
	if(tcgetattr(fd, &oldtio)!=0) {
		perror("setup serial 1");
		return -1;
	}

	memset(&newtio, 0 , sizeof(newtio));

	newtio.c_cflag |= CLOCAL|CREAD;
	newtio.c_cflag &=~CSIZE;
	switch(nbits){
		case 7:
			newtio.c_cflag |= CS7;
			break;
		case 8:
			newtio.c_cflag |= CS8;
			break;
	}
	switch(nevent){
		case 'O':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag |= PARODD;
 			newtio.c_iflag |= (INPCK|ISTRIP);
			break;
		case 'E':
			newtio.c_cflag |= PARENB;
			newtio.c_cflag &=~PARODD;
			newtio.c_iflag |=(INPCK|ISTRIP);
			break;
		case 'N':
			newtio.c_cflag &=~PARENB;
			break;
	}

	switch(nspeed){
		case 2400:
			cfsetospeed(&newtio, B2400);
			break;
		case 4800:
			cfsetospeed(&newtio, B4800);
			break;
		case 9600:
			cfsetispeed(&newtio, B9600);
			break;
		case 19200:
			cfsetispeed(&newtio, B19200);
			break;
		case 115200:
			cfsetispeed(&newtio, B115200);
			break;
		default :
			cfsetospeed(&newtio, B9600);
	}

	if(nstop == 1)
		newtio.c_cflag &=~CSTOPB;
	else 
	if(nstop == 2)
		newtio.c_cflag |=CSTOPB;

	newtio.c_iflag &= ~ICRNL;//禁止将输入中的回车翻译为新行 (除非设置了 IGNCR)
	newtio.c_iflag &= ~ISTRIP;//禁止将所有接收的字符裁减为7比特

	newtio.c_cc[VTIME]= 0;
	newtio.c_cc[VMIN] = 1;

	tcflush(fd, TCIFLUSH);
	if(tcsetattr(fd, TCSANOW, &newtio) != 0){
		perror("com set error.\r\n");
		return -1;
	}
	
	return 0;
}


void * hal_thread(void * arg)
{
	unsigned char databuf[512] = {0};
	int datalen = 0;
	ttyfd = open(arg,O_SYNC|O_RDWR|O_NOCTTY) ;
	if (ttyfd < 0) {
		printf("cannot open this usart device:%s\r\n",(char *)arg);
		exit(0) ;
	}

	if (0 != usart_set(ttyfd,9600,8,'N',1)) {
		printf("set serial error.\r\n");
		exit(0);
	}

	printf("%s :standby...\r\n",(char *)arg);
	while(1) {
		datalen = read(ttyfd,databuf,sizeof(databuf));
		if (datalen < 1) {
			printf("read error\r\n");
			exit(0);
		}
		
		#if HAL_DEBUG
		printf("rx %d bytes\n", datalen);
		for (int i = 0; i < datalen ; ) {
			printf("%02x ", databuf[i]);
			if (((++i) % 20) == 0) 
				printf("\r\n");
		}
		printf("\r\n\r\n");
		#endif
		iec10x_recv(&demobus,databuf,datalen);
	}
}


void * iec10x_loop(void * arg)
{
	struct timeval timer100ms ;
	size_t time = 0;
	while(1) {
		timer100ms.tv_sec = 0;
		timer100ms.tv_usec = 100000 ;
		select(0, NULL, NULL, NULL, &timer100ms);
		time += 100;

		// 处理协议栈 100ms 超时/定时任务，此处不需要太精确
		iec10x_periodic_handle(time); 
	}
}


/**
  * @author   古么宁 
  * @brief    单点控制接口
  * @param    devaddr  : 设备地址，即控制哪条总线的设备
  * @param    COT      : 传输原因
  * @param    num      : 数据结构体 info 的个数，此处一般为 1  
  * @param    addrlist : 信息结构体地址列表
  * @param    info     : 数据结构体数组，总数为 num 个
  * @return   操作成功返回 0 ，否则返回非 0
*/
int32_t SIQsctrl(void * bus, int COT , int num, uint32_t * addrlist , void * data) 
{
	unsigned char * ctrlcmd = data;

	if (*ctrlcmd & 0x80)
	printf("select [0x%x] as [0x%x].\r\n",addrlist[0],*ctrlcmd);
	else
	printf("set [0x%x] as [0x%x].\r\n",addrlist[0],*ctrlcmd);
	return 0;
}

void user_init(iec10x_t * bus)
{
	// 从机接收到主机的单点遥控命令响应
	iec10x_devif_register(bus,C_SC_NA_1 ,NULL , NULL , SIQsctrl); // 单点遥控命令设备接口，
	iec10x_devif_register(bus,M_ME_NC_1 ,STD_info , STD_get , NULL ); // 单点遥控命令设备接口，
	iec10xslv_summon_add(bus,IEC_GRP_ALL,M_ME_NC_1);
}

int main(int argc, char *argv[])
{
	pthread_t newpthread ;
	int size = sizeof(iec10x_t);
	if (argc < 2) {
		printf("Usage:cmd [/dev/ttySx]\r\n");
		return -1;
	}

	printf("demo iec101(%d).\r\n",size);

	#if 0
		/* 平衡模式下 */
		cfg.haltx       = iec10x_puts;
		cfg.print       = printf;
		cfg.cmnaddrlen  = 2;
		cfg.causelen    = 2;
		cfg.lnkaddrlen  = 2;
		cfg.infoaddrlen = 3;
		cfg.lnkaddr     = 1;
		cfg.balance     = 1;
		cfg.mode        = 0;
	#else
		cfg.haltx       = iec10x_puts;
		cfg.print       = printf;
		cfg.dbglevel    = 1;
		cfg.cmnaddrlen  = 1;
		cfg.causelen    = 1;
		cfg.lnkaddrlen  = 1;
		cfg.infoaddrlen = 2;
		cfg.balance     = 0;
		cfg.mode        = 0;
	#endif

	iec10x_protocol_init();

	iec10x_bus_init(&demobus,101,&cfg,user_init);

	pthread_create(&newpthread,NULL,hal_thread,argv[1]);
	pthread_create(&newpthread,NULL,iec10x_loop,NULL);

	getchar();

	return 0;
}
